////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2016-2018 Qualcomm Technologies, Inc.
// All Rights Reserved.
// Confidential and Proprietary - Qualcomm Technologies, Inc.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// @file camxhal3metadatautil.cpp
/// @brief Implements HAL3MetadataUtil methods. This will have Android camera_metadata signature all over
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <system/camera_metadata.h>

#include "camxhal3metadatatags.h"
#include "camxhal3metadatatagtypes.h"
#include "camxhal3metadatautil.h"
#include "camxhal3module.h"
#include "camxhwcontext.h"
#include "camxincs.h"
#include "camxvendortags.h"

CAMX_NAMESPACE_BEGIN

// Definition of static variable declared in class
UINT32 HAL3MetadataUtil::s_allTags[MaxMetadataTags];

UINT32 HAL3MetadataUtil::s_vendorTags[MaxMetadataTags];

UINT32 HAL3MetadataUtil::s_staticTags[MaxMetadataTags];

SIZE_T HAL3MetadataUtil::s_tagOffset[MaxMetadataTags];

SIZE_T HAL3MetadataUtil::s_maxMetadataSize;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// GetMetadataType
///
/// @brief Static method to convert camx Metadata to Android type
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static CAMX_INLINE camera_metadata* GetMetadataType(
    Metadata* pMetadata)
{
    return static_cast<camera_metadata*>(pMetadata);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Constants
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// Android returns the following values from its camera_metadata APIs, but doesn't
// define them in header.So there is no option but to shadow them here.
static const INT32 MetadataUtilOK       = 0;
static const INT32 MetadataUtilError    = 1;
static const INT32 MetadataUtilNotFound = -ENOENT;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::CalculateLinearOffsets
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID HAL3MetadataUtil::CalculateLinearOffsets()
{
    UINT32 numVendorTags = VendorTagManager::GetAllTagCount();
    UINT32 lastGTagIndex  = MaxMetadataTagCount - 1;

    // Populate table of linear offsets if every supported tag is used
    // Currently used for the sticky logic in the metadata pools.
    s_tagOffset[0] = 0;

    UINT align = static_cast<UINT>(alignof(MAXALIGN_T));

    for (UINT i = 0; i < MaxMetadataTagCount - 1; i++)
    {
        // Each tag is offset from the previous (s_tagOffset[i]) by its size (the factor of the below)
        s_tagOffset[i + 1] = Utils::ByteAlign(s_tagOffset[i] + (TagElements[i] *
                                                                TagFactor[i]   *
                                                                GetSizeByType(TagType[i])), align);
    }


    if (0 < numVendorTags)
    {
        UINT offset = MaxMetadataTagCount;


        // First one special, as it uses the sizes from the google tag tables
        s_tagOffset[offset] = Utils::ByteAlign(s_tagOffset[offset - 1] +
                                               (TagElements[lastGTagIndex] *
                                                TagFactor[lastGTagIndex]   *
                                                GetSizeByType(TagType[lastGTagIndex])), align);

        for (UINT i = 0; i < numVendorTags - 1; i++)
        {
            // last + sizeof vendor tag
            s_tagOffset[offset + 1] = Utils::ByteAlign(s_tagOffset[offset] +
                                                       VendorTagManager::VendorTagSize(i), align);
            offset++;
        }

        s_maxMetadataSize = Utils::ByteAlign(s_tagOffset[offset] +
                                             VendorTagManager::VendorTagSize(numVendorTags - 1), align);
    }
    else
    {
        s_maxMetadataSize = Utils::ByteAlign(s_tagOffset[lastGTagIndex] +
                                             (TagElements[lastGTagIndex] *
                                              TagFactor[lastGTagIndex]   *
                                              GetSizeByType(TagType[lastGTagIndex])), align);
    }
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::CalculateSizeAllMeta
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID HAL3MetadataUtil::CalculateSizeAllMeta(
    SIZE_T* pEntryCapacity,
    SIZE_T* pDataSize)
{
    CamxResult                result          = CamxResultSuccess;
    UINT32                    i               = 0;
    UINT32                    j               = 0;
    UINT32                    k               = 0;
    SIZE_T                    smallIndex      = 0;
    SIZE_T                    bigIndex        = 0;
    UINT32                    vendorTagCount  = 0;
    StaticMetadataKeysInfo    requestKeysInfo = { 0 };
    StaticMetadataKeysInfo    resultKeysInfo  = { 0 };
    const HwEnvironment*      pHwEnvironment  = HwEnvironment::GetInstance();

    result = pHwEnvironment->GetStaticMetadataKeysInfo(&requestKeysInfo, RequestAvailableRequestKeys);

    if (CamxResultSuccess == result)
    {
        result = pHwEnvironment->GetStaticMetadataKeysInfo(&resultKeysInfo, RequestAvailableResultKeys);
    }

    if (CamxResultSuccess == result)
    {
        SIZE_T                    requestKeySize    = requestKeysInfo.numKeys;
        SIZE_T                    resultKeySize     = resultKeysInfo.numKeys;

        const CameraMetadataTag*  pRequestKeys      = requestKeysInfo.pStaticKeys;
        const CameraMetadataTag*  pResultKeys       = resultKeysInfo.pStaticKeys;
        const CameraMetadataTag*  pSmallArray       = NULL;
        const CameraMetadataTag*  pBigArray         = NULL;

        // Total size reserved for metadata tags will be the superset of the sizes of Request and Result tags + Vendor tags

        if (requestKeySize > resultKeySize)
        {
            bigIndex    = requestKeySize;
            smallIndex  = resultKeySize;
            pBigArray   = &pRequestKeys[0];
            pSmallArray = &pResultKeys[0];
        }
        else
        {
            bigIndex    = resultKeySize;
            smallIndex  = requestKeySize;
            pBigArray   = &pResultKeys[0];
            pSmallArray = &pRequestKeys[0];
        }

        *pEntryCapacity = bigIndex;
        *pDataSize = 0;
        // Initially, pDataSize = sum of max sizes of all keys in big array
        for (i = 0; i < bigIndex; i++)
        {
            *pDataSize += GetMaxSizeByTag(pBigArray[i]);
            HAL3MetadataUtil::s_allTags[i] = static_cast<UINT32>(pBigArray[i]);
        }

        for (i = 0; i < smallIndex; i++)
        {
            for (j = 0; j < bigIndex; j++)
            {
                if (pSmallArray[i] == pBigArray[j])
                {
                    break;
                }
            }
            if (j == bigIndex)
            {
                // pDataSize += max size of key at pSmallArray[i]
                *pDataSize += GetMaxSizeByTag(pSmallArray[i]);
                s_allTags[*pEntryCapacity] = static_cast<UINT32>(pSmallArray[i]);
                (*pEntryCapacity)++;
            }
        }

        vendorTagCount = VendorTagManager::GetTagCount(TagSectionVisibility::TagSectionVisibleToAll);
        CAMX_ASSERT(MaxMetadataTags >= vendorTagCount);
        if ((vendorTagCount > 0) &&
            (vendorTagCount < MaxMetadataTags))
        {
            VendorTagManager::GetAllTags(s_vendorTags, TagSectionVisibility::TagSectionVisibleToAll);
            for (k = 0; k < vendorTagCount; k++)
            {
                HAL3MetadataUtil::s_allTags[*pEntryCapacity] = static_cast<UINT32>(s_vendorTags[k]);
                (*pEntryCapacity)++;
            }
        }
        else
        {
            if (vendorTagCount >= MaxMetadataTags)
            {
                CAMX_LOG_ERROR(CamxLogGroupHAL, "VendorTagCount :%d not matching criteria", vendorTagCount);
            }
        }

        CAMX_ASSERT(MaxMetadataTags >= *pEntryCapacity);

        // Now we add the Vendor Tag Blob
        *pDataSize      += VendorTagManager::GetVendorTagBlobSize();

        // Also populate Mapped IDs
        VendorTagManager::PopulateControlVendorTagId();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::CalculateSizeStaticMeta
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID HAL3MetadataUtil::CalculateSizeStaticMeta(
    SIZE_T* pEntryCapacity,
    SIZE_T* pDataSize)
{
    CamxResult              result                  = CamxResultSuccess;
    StaticMetadataKeysInfo  characteristicsKeysInfo = { 0 };
    UINT32                  vendorTagCount          = 0;
    UINT32                  k                       = 0;
    const HwEnvironment*    pHwEnvironment          = HwEnvironment::GetInstance();

    result = pHwEnvironment->GetStaticMetadataKeysInfo(&characteristicsKeysInfo, RequestAvailableCharacteristicsKeys);

    if (CamxResultSuccess == result)
    {
        SIZE_T                    characteristicsKeysSize = characteristicsKeysInfo.numKeys;
        const CameraMetadataTag*  pCharacteristicsKeys    = characteristicsKeysInfo.pStaticKeys;

        *pEntryCapacity = 0;
        *pDataSize      = 0;

        // Initially, pDataSize = sum of max sizes of all keys in big array
        for (UINT32 i = 0; i < characteristicsKeysSize; i++)
        {
            (*pEntryCapacity)++;
            *pDataSize += GetMaxSizeByTag(pCharacteristicsKeys[i]);
            HAL3MetadataUtil::s_staticTags[i] = static_cast<UINT32>(pCharacteristicsKeys[i]);
        }

        vendorTagCount = VendorTagManager::GetTagCount(TagSectionVisibility::TagSectionVisibleToAll);
        CAMX_ASSERT(MaxMetadataTags - characteristicsKeysSize > vendorTagCount);
        if ((vendorTagCount > 0) &&
            (vendorTagCount < MaxMetadataTags))
        {
            VendorTagManager::GetAllTags(s_vendorTags, TagSectionVisibility::TagSectionVisibleToAll);
            for (k = 0; k < vendorTagCount; k++)
            {
                HAL3MetadataUtil::s_allTags[*pEntryCapacity] = static_cast<UINT32>(s_vendorTags[k]);
                (*pEntryCapacity)++;
            }
        }
        else
        {
            if (vendorTagCount >= MaxMetadataTags)
            {
                CAMX_LOG_ERROR(CamxLogGroupHAL, "VendorTagCount :%d not matching criteria", vendorTagCount);
            }
        }

        CAMX_ASSERT(MaxMetadataTags >= *pEntryCapacity);

        // Now we add the Vendor Tag Blob
        *pDataSize += VendorTagManager::GetVendorTagBlobSize();
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::CreateMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
Metadata* HAL3MetadataUtil::CreateMetadata(
    SIZE_T entryCapacity,
    SIZE_T dataSize)
{
    Metadata* pMetadata = allocate_camera_metadata(entryCapacity, dataSize);

    return pMetadata;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::FreeMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID HAL3MetadataUtil::FreeMetadata(
    Metadata* pMetadata)
{
    if (NULL != pMetadata)
    {
        free_camera_metadata(GetMetadataType(pMetadata));
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::ResetMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID HAL3MetadataUtil::ResetMetadata(
    Metadata* pMetadata,
    SIZE_T    dataCapacity,
    SIZE_T    entryCapacity)
{
    place_camera_metadata(pMetadata, 0xffffffff, entryCapacity, dataCapacity);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::GetMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult HAL3MetadataUtil::GetMetadata(
    Metadata*   pMetadata,
    UINT32      tag,
    VOID**      ppData)
{
    camera_metadata_entry_t entry;
    CamxResult              result = CamxResultEFailed;

    *ppData = NULL;

    // Remove type identifer from tag
    tag &= ~DriverInternalGroupMask;

    if (MetadataUtilOK == find_camera_metadata_entry(GetMetadataType(pMetadata), tag, &entry))
    {
        UINT8 type = HAL3MetadataUtil::GetTypeByTag(tag);

        switch(type)
        {
            case TYPE_BYTE:
                *ppData = static_cast<VOID*>(entry.data.u8);
                break;
            case TYPE_INT32:
                *ppData = static_cast<VOID*>(entry.data.i32);
                break;
            case TYPE_FLOAT:
                *ppData = static_cast<VOID*>(entry.data.f);
                break;
            case TYPE_INT64:
                *ppData = static_cast<VOID*>(entry.data.i64);
                break;
            case TYPE_DOUBLE:
                *ppData = static_cast<VOID*>(entry.data.d);
                break;
            case TYPE_RATIONAL:
                *ppData = static_cast<VOID*>(entry.data.r);
                break;
            default:
                break;
        }
        result = CamxResultSuccess;
    }
    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::GetMetadataCount
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SIZE_T HAL3MetadataUtil::GetMetadataCount(
    Metadata*   pMetadata,
    UINT32      tag)
{
    camera_metadata_entry_t entry;
    SIZE_T                  count = 0;

    // Remove type identifer from tag
    tag &= ~DriverInternalGroupMask;

    if (MetadataUtilOK == find_camera_metadata_entry(GetMetadataType(pMetadata), tag, &entry))
    {
        count = entry.count;
    }
    return count;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::GetMetadataByIndex
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID HAL3MetadataUtil::GetMetadataByIndex(
    Metadata*   pMetadata,
    SIZE_T      index,
    UINT32*     pTag,
    SIZE_T*     pCount,
    VOID**      ppData)
{
    camera_metadata_entry_t entry = {0};

    *ppData = NULL;
    *pTag   = MaxMetadataTags;

    if (MetadataUtilOK == get_camera_metadata_entry(static_cast<camera_metadata*>(pMetadata), index, &entry))
    {
        *pTag   = entry.tag;
        *pCount = entry.count;

        UINT8 type = HAL3MetadataUtil::GetTypeByTag(entry.tag);

        switch(type)
        {
            case TYPE_BYTE:
                *ppData = static_cast<VOID*>(entry.data.u8);
                break;
            case TYPE_INT32:
                *ppData = static_cast<VOID*>(entry.data.i32);
                break;
            case TYPE_FLOAT:
                *ppData = static_cast<VOID*>(entry.data.f);
                break;
            case TYPE_INT64:
                *ppData = static_cast<VOID*>(entry.data.i64);
                break;
            case TYPE_DOUBLE:
                *ppData = static_cast<VOID*>(entry.data.d);
                break;
            case TYPE_RATIONAL:
                *ppData = static_cast<VOID*>(entry.data.r);
                break;
            default:
                break;
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::MergeMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult HAL3MetadataUtil::MergeMetadata(
    Metadata*       pDstMetadata,
    const Metadata* pSrcMetadata)
{
    CamxResult         result             = CamxResultSuccess;
    INT                status             = 0;
    camera_metadata_t* pMergeSrcMetadata  =
        // NOWHINE CP036a: Non-const required by standard function
        const_cast<camera_metadata_t*>(static_cast<const camera_metadata_t*>(pSrcMetadata));
    camera_metadata_t* pMergeDstMetadata  =
        // NOWHINE CP036a: Non-const required by standard function
        const_cast<camera_metadata_t*>(static_cast<const camera_metadata_t*>(pDstMetadata));

    SIZE_T totalEntries = get_camera_metadata_entry_count(pMergeSrcMetadata);

    for (UINT i = 0; i < totalEntries; i++)
    {
        camera_metadata_entry_t srcEntry;
        camera_metadata_entry_t dstEntry;
        camera_metadata_entry_t updatedEntry;

        get_camera_metadata_entry(pMergeSrcMetadata, i, &srcEntry);

        status = find_camera_metadata_entry(pMergeDstMetadata, srcEntry.tag, &dstEntry);

        if (MetadataUtilOK != status)
        {
            status = add_camera_metadata_entry(GetMetadataType(pMergeDstMetadata),
                                               srcEntry.tag,
                                               srcEntry.data.i32,
                                               srcEntry.count);
        }
        else
        {
            status = update_camera_metadata_entry(GetMetadataType(pMergeDstMetadata),
                                                  dstEntry.index,
                                                  srcEntry.data.i32,
                                                  srcEntry.count,
                                                  &updatedEntry);
        }

        if (MetadataUtilOK != status)
        {
            result = CamxResultEFailed;
            break;
        }
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::AppendMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult HAL3MetadataUtil::AppendMetadata(
    Metadata*       pDstMetadata,
    const Metadata* pSrcMetadata)
{
    CamxResult result = CamxResultSuccess;
    INT status;
    // NOWHINE CP036a: Since google function is non-const, had to add the const_cast
    status = append_camera_metadata(reinterpret_cast<camera_metadata_t*>(const_cast<Metadata*>(pDstMetadata)),
                                    static_cast<const camera_metadata_t*>(pSrcMetadata));

    if (status != 0)
    {
        result = CamxResultEFailed;
        CAMX_LOG_ERROR(CamxLogGroupHAL, "Could not add append src metadata to dst with status = %d", status);
    }

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::UpdateMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CamxResult HAL3MetadataUtil::UpdateMetadata(
    Metadata*   pMetadata,
    UINT32      tag,
    const VOID* pData,
    SIZE_T      count,
    BOOL        updateAllowed)
{
    INT32       status  = MetadataUtilError;
    CamxResult  result  = CamxResultEFailed;

    // Remove type identifer from tag
    tag &= ~DriverInternalGroupMask;

    INT32       type       = get_camera_metadata_tag_type(tag);
    SIZE_T      bufferSize = get_camera_metadata_size(GetMetadataType(pMetadata));
    UINTPTR_T   bufAddr    = reinterpret_cast<UINTPTR_T>(pMetadata);
    UINTPTR_T   dataAddr   = reinterpret_cast<UINTPTR_T>(pData);

    if ((-1 == type) ||
        ((dataAddr > bufAddr) && (dataAddr < (bufAddr + bufferSize))) ||
        (0 == GetSizeByType(GetTypeByTag(tag))))
    {
        CAMX_LOG_ERROR(CamxLogGroupHAL, "Cannot set Metadata for Tag: 0x%x ", tag);
        return result;
    }

    camera_metadata_entry_t entry;

    // We do not need to worry about the size of the overall metadata, as we have already allocated max size
    CAMX_ASSERT_MESSAGE(count <= (GetMaxSizeByTag(tag) / GetSizeByType(GetTypeByTag(tag))),
                        "Tag %08x (type: %d, index: %d) too large for allocated size: tag size: %d, tag type size: %d, "
                        "count %d > elements allocated: %d",
                        tag, GetTypeByTag(tag), CAMX_TAG_TO_INDEX(tag),
                        GetMaxSizeByTag(tag), GetSizeByType(GetTypeByTag(tag)),
                        count, GetMaxSizeByTag(tag) / GetSizeByType(GetTypeByTag(tag)));

    status = find_camera_metadata_entry(GetMetadataType(pMetadata), tag, &entry);
    // As previous reference to metadata are invalidated, if a meta value change from nonzero to zero sized data,
    // add metadata with count 0.
    if ((MetadataUtilNotFound == status))
    {
        status = add_camera_metadata_entry(GetMetadataType(pMetadata), tag, pData, count);
    }
    else if (MetadataUtilOK == status)
    {
        if (FALSE == updateAllowed)
        {
            SIZE_T sizeOfTag = GetSizeByType(GetTypeByTag(tag));

            if ((count != entry.count) ||
                (0 != Utils::Memcmp(reinterpret_cast<VOID*>(entry.data.u8), pData, sizeOfTag * count)))
            {
                CAMX_LOG_ERROR(CamxLogGroupCore, "DUPLICATE tag write with different data for %08x!", tag);

                CAMX_LOG_ERROR(CamxLogGroupCore, "=======");
                for (UINT i = 0; i < count; i++)
                {
                    switch(sizeOfTag)
                    {
                        case 1:
                            CAMX_LOG_ERROR(CamxLogGroupCore,
                                           " %016hhx | %016hhx",
                                           reinterpret_cast<const BYTE*>(entry.data.u8)[i],
                                           reinterpret_cast<const BYTE*>(pData)[i]);
                            break;
                        case 4:
                            CAMX_LOG_ERROR(CamxLogGroupCore,
                                           " %016x | %016x",
                                           reinterpret_cast<const UINT32*>(entry.data.u8)[i],
                                           reinterpret_cast<const UINT32*>(pData)[i]);
                            break;
                        case 8:
                            CAMX_LOG_ERROR(CamxLogGroupCore,
                                           " %016llx | %016llx",
                                           reinterpret_cast<const UINT64*>(entry.data.u8)[i],
                                           reinterpret_cast<const UINT64*>(pData)[i]);
                            break;
                        default:
                            CAMX_LOG_ERROR(CamxLogGroupCore, "Unsupported Tag Size");
                            break;
                    }
                }
                CAMX_LOG_ERROR(CamxLogGroupCore, "-------");
                CAMX_LOG_ERROR(CamxLogGroupCore, "Old data retained to prevent metadata issues. Please resolve conflicts");

                status = CamxResultEFailed;
            }
        }
        else
        {
            if ((0 != entry.count) && (0 == count))
            {
                // We have an existing entry with count not 0, and now need to update with count=0.
                // In this case, need to delete the entry and add it back with count=0,
                // otherwise offset will not be updated to 0 and thus validation fails.
                status = delete_camera_metadata_entry(GetMetadataType(pMetadata), entry.index);
                if (MetadataUtilOK == status)
                {
                    status = add_camera_metadata_entry(GetMetadataType(pMetadata), tag, pData, count);
                }
            }
            else
            {
                status = update_camera_metadata_entry(GetMetadataType(pMetadata), entry.index, pData, count, NULL);
            }
        }
    }

    result = (MetadataUtilOK == status) ? CamxResultSuccess : CamxResultEFailed;

    return result;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::GetPropertyBlob
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID HAL3MetadataUtil::GetPropertyBlob(
    Metadata*           pMetadata,
    MainPropertyBlob**  ppBlob)
{
    CamxResult result = CamxResultSuccess;

    camera_metadata_entry_t entry;

    // If we do not find the private tag in the metadata, we need to add it
    if (MetadataUtilNotFound == find_camera_metadata_entry(GetMetadataType(pMetadata), VendorTagPrivate, &entry))
    {
        INT32            status = MetadataUtilError;
        MainPropertyBlob blob   = {};

        // add will fail if set_vendor_tag_query_ops() has NOT been called first by the application
        status = add_camera_metadata_entry(GetMetadataType(pMetadata), VendorTagPrivate, &blob, sizeof(MainPropertyBlob));
        if (MetadataUtilOK != status)
        {
            result = CamxResultEFailed;
        }
        else
        {
            // we are guaranteed to find the tag here
            status = find_camera_metadata_entry(GetMetadataType(pMetadata), VendorTagPrivate, &entry);
            CAMX_ASSERT(MetadataUtilOK == status);
        }
    }

    if (CamxResultSuccess == result)
    {
        *ppBlob = reinterpret_cast<MainPropertyBlob*>(entry.data.u8);
    }

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::GetMaxSizeByTag
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SIZE_T HAL3MetadataUtil::GetMaxSizeByTag(
    UINT32 tag)
{
    SIZE_T index = CAMX_TAG_TO_INDEX(tag);
    SIZE_T size;

    if (MaxMetadataTagCount > index)
    {
        // Remove type identifer from tag
        tag &= ~DriverInternalGroupMask;

        size = GetSizeByType(GetTypeByTag(tag)) * GetNumElementsByTag(tag);

        if (0 == TagFactor[index])
        {
            const HwEnvironment* pHwEnvironment = HwEnvironment::GetInstance();

            StaticMetadataKeysInfo keyInfo = {0};
            CamxResult             result  = pHwEnvironment->GetStaticMetadataKeysInfo(&keyInfo,
                                                                                       static_cast<CameraMetadataTag>(tag));

            if (CamxResultSuccess == result)
            {
                size *= keyInfo.numKeys;
            }
        }
        else if (1 != TagFactor[index])
        {
            size *= TagFactor[index];
        }
    }
    else
    {
        size = VendorTagManager::VendorTagSize(index - MaxMetadataTagCount);
    }

    return size;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::GetSizeByType
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SIZE_T HAL3MetadataUtil::GetSizeByType(
    UINT8 type)
{
    SIZE_T size = 0;

    switch (type)
    {
        case TYPE_BYTE:
            size = sizeof(UINT8);
            break;

        case TYPE_INT32:
            size = sizeof(UINT32);
            break;

        case TYPE_FLOAT:
            size = sizeof(FLOAT);
            break;

        case TYPE_INT64:
            size = sizeof(UINT64);
            break;

        case TYPE_DOUBLE:
            size = sizeof(DOUBLE);
            break;

        case TYPE_RATIONAL:
            size = sizeof(camera_metadata_rational_t);
            break;

        default:
            CAMX_ASSERT_ALWAYS_MESSAGE("Got unsupported type %d", type);
            break;
    }

    return size;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::DebugDataSize
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SIZE_T HAL3MetadataUtil::DebugDataSize(
    DebugDataType debugDataType)
{
    const StaticSettings*   pStaticSettings = HwEnvironment::GetInstance()->GetStaticSettings();
    SIZE_T                  debugDataSize   = 0;

    // Get 3A size only if enable
    if (TRUE == pStaticSettings->enable3ADebugData)
    {
        switch (debugDataType)
        {
            case DebugDataType::AEC:
                debugDataSize = pStaticSettings->debugDataSizeAEC;
                break;
            case DebugDataType::AWB:
                debugDataSize = pStaticSettings->debugDataSizeAWB;
                break;
            case DebugDataType::AF:
                debugDataSize = pStaticSettings->debugDataSizeAF;
                break;
            default:
                break;
        }
    }

    // Get tuning size if enable
    if (TRUE == pStaticSettings->enableTuningMetadata)
    {
        switch (debugDataType)
        {
            case DebugDataType::IFETuning:
                debugDataSize = pStaticSettings->tuningDumpDataSizeIFE;
                break;
            case DebugDataType::IPETuning:
                debugDataSize = pStaticSettings->tuningDumpDataSizeIPE;
                break;
            case DebugDataType::BPSTuning:
                debugDataSize = pStaticSettings->tuningDumpDataSizeBPS;
                break;
            default:
                break;
        }
    }

    // Get total size
    if (DebugDataType::MaxTypes == debugDataType)
    {
        SIZE_T size3A       = 0;
        SIZE_T sizeTuning   = 0;

        if (TRUE == pStaticSettings->enable3ADebugData)
        {
            size3A = pStaticSettings->debugDataSizeAEC +
                     pStaticSettings->debugDataSizeAWB +
                     pStaticSettings->debugDataSizeAF;
        }

        if (TRUE == pStaticSettings->enableTuningMetadata)
        {
            sizeTuning = pStaticSettings->tuningDumpDataSizeIFE +
                         pStaticSettings->tuningDumpDataSizeIPE +
                         pStaticSettings->tuningDumpDataSizeBPS;
        }

        debugDataSize = size3A + sizeTuning;
    }

    return debugDataSize;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::DebugDataOffset
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
SIZE_T HAL3MetadataUtil::DebugDataOffset(
    DebugDataType debugDataType)
{
    SIZE_T                  offset          = 0;
    const StaticSettings*   pStaticSettings = HwEnvironment::GetInstance()->GetStaticSettings();
    SIZE_T                  sizeAEC         = 0;
    SIZE_T                  sizeAWB         = 0;
    SIZE_T                  sizeAF          = 0;
    SIZE_T                  sizeIFE         = 0;
    SIZE_T                  sizeIPE         = 0;
    SIZE_T                  sizeBPS         = 0;

    // Get 3A size only if enable
    if (TRUE == pStaticSettings->enable3ADebugData)
    {
        sizeAEC = pStaticSettings->debugDataSizeAEC;
        sizeAWB = pStaticSettings->debugDataSizeAWB;
        sizeAF  = pStaticSettings->debugDataSizeAF;
    }

    // Get tuning size if enable
    if (TRUE == pStaticSettings->enableTuningMetadata)
    {
        sizeIFE = pStaticSettings->tuningDumpDataSizeIFE;
        sizeIPE = pStaticSettings->tuningDumpDataSizeIPE;
        sizeBPS = pStaticSettings->tuningDumpDataSizeBPS;
    }

    switch (debugDataType)
    {
        case DebugDataType::AEC:
            offset = 0;
            break;
        case DebugDataType::AWB:
            offset = sizeAEC;
            break;
        case DebugDataType::AF:
            offset = sizeAEC + sizeAWB;
            break;
        case DebugDataType::IFETuning:
            offset = sizeAEC + sizeAWB + sizeAF;
            break;
        case DebugDataType::IPETuning:
            offset = sizeAEC + sizeAWB + sizeAF + sizeIFE;
            break;
        case DebugDataType::BPSTuning:
            offset = sizeAEC + sizeAWB + sizeAF + sizeIFE + sizeIPE;
            break;
        default:
            CAMX_ASSERT_ALWAYS_MESSAGE("Not supported data type");
            break;
    };

    return offset;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// CamxPropertyIDToString
///
/// @brief  Returns a pointer to a const string which is a human readible representation of the PropertyID
///
/// @param  id  Tag to stringize
///
/// @return String version of the tag or NULL if not valid
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CAMX_INLINE const CHAR* CamxPropertyIDToString(
    PropertyID      id)
{
    const CHAR* pTagName = NULL;

    switch (id >> 16)
    {
        case static_cast<UINT32>(PropertyGroup::Result) :
            if ((id & ~PropertyIDPerFrameResultBegin) < CAMX_ARRAY_SIZE(pMainPropertyStrings))
            {
                pTagName = pMainPropertyStrings[id & ~PropertyIDPerFrameResultBegin];
            }
            break;
        case static_cast<UINT32>(PropertyGroup::Internal) :
            if ((id & ~PropertyIDPerFrameInternalBegin) < CAMX_ARRAY_SIZE(pInternalPropertyStrings))
            {
                pTagName = pInternalPropertyStrings[id & ~PropertyIDPerFrameInternalBegin];
            }
            break;
        case static_cast<UINT32>(PropertyGroup::Usecase) :
            if ((id & ~PropertyIDUsecaseBegin) < CAMX_ARRAY_SIZE(pUsecasePropertyStrings))
            {
                pTagName = pUsecasePropertyStrings[id & ~PropertyIDUsecaseBegin];
            }
            break;
        case static_cast<UINT32>(PropertyGroup::DebugData) :
            if ((id & ~PropertyIDPerFrameDebugDataBegin) < CAMX_ARRAY_SIZE(pDebugDataPropertyStrings))
            {
                pTagName = pDebugDataPropertyStrings[id & ~PropertyIDPerFrameDebugDataBegin];
            }
            break;
        default:
            // Unknown PropertyID
            CAMX_ASSERT_ALWAYS_MESSAGE("Unknown PropertyID %08x", id);
            break;
    }

    return pTagName;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::GetNameByTag
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID HAL3MetadataUtil::GetNameByTag(
    CHAR*       pDst,
    SIZE_T      sizeDst,
    UINT32      tag)
{
    const CHAR* pPropertyPrefix     = "";
    const CHAR* pPropertyName       = "";

    if (tag != InvalidIndex)
    {
        // Print based on the section
        UINT32 section = tag >> 16;
        if ((section & VENDOR_SECTION) != 0)
        {
            pPropertyName   = VendorTagManager::GetTagName(tag);
            pPropertyPrefix = VendorTagManager::GetSectionName(tag);
            OsUtils::SNPrintF(pDst, sizeDst, "%s.%s", pPropertyPrefix, pPropertyName);
        }
        else if ((section == static_cast<UINT32>(PropertyGroup::Result)) ||
                 (section == static_cast<UINT32>(PropertyGroup::Internal)) ||
                 (section == static_cast<UINT32>(PropertyGroup::Usecase)) ||
                 (section == static_cast<UINT32>(PropertyGroup::DebugData)))
        {
            pPropertyName = CamxPropertyIDToString(tag);
            if (NULL != pPropertyName)
            {
                OsUtils::StrLCpy(pDst, CamxPropertyIDToString(tag), sizeDst);
            }
        }
        else
        {
            // MetadataPoolSection::Input, MetadataPoolSection::Usecase, MetadataPoolSection::Static, or default
            CameraMetadataTagToString(static_cast<CameraMetadataTag>(tag), &pPropertyPrefix, &pPropertyName);
            OsUtils::SNPrintF(pDst, sizeDst, "%s%s", pPropertyPrefix, pPropertyName);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// HAL3MetadataUtil::GetPropertyName
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const CHAR* HAL3MetadataUtil::GetPropertyName(
    UINT32      tag)
{
    const CHAR* pName = "";

    if (tag != InvalidIndex)
    {
        pName = CamxPropertyIDToString(tag);
    }

    return pName;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// HAL3MetadataUtil::DumpMetadata
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID HAL3MetadataUtil::DumpMetadata(
    const Metadata* pMetadata)
{
    if (NULL != pMetadata)
    {
        const camera_metadata* pAndroidMeta = static_cast<const camera_metadata*>(pMetadata);

        for (UINT i = 0; i < get_camera_metadata_entry_count(pAndroidMeta); i++)
        {
            camera_metadata_ro_entry_t entry = { 0 };
            get_camera_metadata_ro_entry(pAndroidMeta, i, &entry);
            PrintTagData(pMetadata, InputMetadataSectionMask | entry.tag);
        }
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// HAL3MetadataUtil::PrintTagData
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
VOID HAL3MetadataUtil::PrintTagData(
    const Metadata* pMetadata,
    UINT32          tag)
{
    const camera_metadata*      pAndroidMeta = static_cast<const camera_metadata*>(pMetadata);
    camera_metadata_ro_entry_t  entry = { 0 };
    const uint8_t*              pData;
    const CHAR*                 pTagName;
    const CHAR*                 pTagSection;
    UINT                        pool = (tag & DriverInternalGroupMask);

    if (((static_cast<UINT32>(PropertyGroup::Result) << 16) == pool)   ||
        ((static_cast<UINT32>(PropertyGroup::Internal) << 16) == pool) ||
        ((static_cast<UINT32>(PropertyGroup::Usecase) << 16) == pool)  ||
        ((static_cast<UINT32>(PropertyGroup::DebugData) << 16) == pool)      )
    {
        pTagName = CamxPropertyIDToString(tag);
        switch (pool)
        {
            case static_cast<UINT32>(PropertyGroup::Result) << 16:
                pTagSection = "Result";
                break;
            case static_cast<UINT32>(PropertyGroup::Internal) << 16:
                pTagSection = "Internal";
                break;
            case static_cast<UINT32>(PropertyGroup::Usecase) << 16:
                pTagSection = "Usecase";
                break;
            case static_cast<UINT32>(PropertyGroup::DebugData) << 16:
                pTagSection = "Debug";
                break;
            default:
                pTagSection = "ERROR";
                break;
        }
        CAMX_LOG_META("    Pool: %s  Tag: %s", pTagSection, pTagName);
        CAMX_LOG_META("         TODO: property data dump");
    }
    else if (tag & 0x80000000)
    {
        CAMX_LOG_META("    Tag: %08x    TODO: vendor tag support", tag);

    }
    else if (MetadataUtilOK == find_camera_metadata_ro_entry(pAndroidMeta, (tag & ~DriverInternalGroupMask), &entry))
    {
        static UINT valuesPerLine[NUM_TYPES] =
        {
            16, // TYPE_BYTE
            4,  // TYPE_INT32
            8,  // TYPE_FLOAT
            2,  // TYPE_INT64
            4,  // TYPE_DOUBLE
            3,  // TYPE_RATIONAL
        };

        if (entry.type < NUM_TYPES)
        {
            size_t          typeSize = camera_metadata_type_size[entry.type];

            pData = entry.data.u8;
            CameraMetadataTagToString(static_cast<CameraMetadataTag>(tag), &pTagSection, &pTagName);

            CHAR        valueString[200];
            UINT        stringOffset;
            SIZE_T      count = entry.count;

            SIZE_T lines = count / valuesPerLine[entry.type];
            if (count % valuesPerLine[entry.type] != 0)
            {
                lines++;
            }

            CAMX_LOG_META("    Pool: %s  Tag: %s  Count: %zd", pTagSection, pTagName, count);
            if (0 == count)
            {
                CAMX_LOG_META("        count == 0, data being removed");
            }

            SIZE_T  index = 0;
            UINT    j;
            UINT    k;
            for (j = 0; j < lines; j++)
            {
                stringOffset = 0;

                for (k = 0;
                     k < valuesPerLine[entry.type] && count > 0;
                     k++, count--, index += typeSize)
                {
                    switch (entry.type)
                    {
                        case TYPE_BYTE:
                            stringOffset += OsUtils::SNPrintF(&valueString[stringOffset],
                                (sizeof(valueString) - stringOffset), "%hhu ", *(pData + index));
                            break;
                        case TYPE_INT32:
                            stringOffset += OsUtils::SNPrintF(&valueString[stringOffset],
                                (sizeof(valueString) - stringOffset), "%d ",
                                *reinterpret_cast<const int32_t*>(pData + index));
                            break;
                        case TYPE_FLOAT:
                            stringOffset += OsUtils::SNPrintF(&valueString[stringOffset],
                                (sizeof(valueString) - stringOffset), "%0.8f ",
                                *reinterpret_cast<const FLOAT*>(pData + index));
                            break;
                        case TYPE_INT64:
                            stringOffset += OsUtils::SNPrintF(&valueString[stringOffset],
                                (sizeof(valueString) - stringOffset), "%lld ",
                                *reinterpret_cast<const int64_t*>(pData + index));
                            break;
                        case TYPE_DOUBLE:
                            stringOffset += OsUtils::SNPrintF(&valueString[stringOffset],
                                (sizeof(valueString) - stringOffset), "%0.8f ",
                                *reinterpret_cast<const DOUBLE*>(pData + index));
                            break;
                        case TYPE_RATIONAL:
                            {
                                int32_t numerator = *reinterpret_cast<const int32_t*>(pData + index);
                                int32_t denominator = *reinterpret_cast<const int32_t*>(pData + index + 4);
                                stringOffset += OsUtils::SNPrintF(&valueString[stringOffset],
                                    (sizeof(valueString) - stringOffset), "(%d / %d) ", numerator, denominator);
                                break;
                            }
                        default:
                            break;
                    }
                }
                CAMX_LOG_META("         %s", valueString);
            }
        }
    }
    else
    {
        CAMX_LOG_META("    ERROR: TAG NOT FOUND: %08x", tag);
    }
}

CAMX_NAMESPACE_END
